API Browser

Union

Result<T, E>

union struct Result<T, E>

Namespace: System
Assembly: Raven.Core.dll
Source file: Result.rvn

Inheritance: Object → ValueType → Result<T, E>
Implements: IPropagatable<Result<T, E>, T, E>

Represents either a successful value (Ok) or an expected failure (Failure). Use Result<T, E> when the caller needs to act on a failure reason. For ordinary absence without an error, use Optional.

Usage

Handle cases with if let ... else

A booking accepts between one and six seats. Validation returns the accepted count or an explanation that the form can display. The if ... else expression constructs the result; an invalid request is an expected outcome.

import System.*
import System.Result.*

func ValidateSeats(requested: int) -> Result<int, string> {
    return if requested >= 1 && requested <= 6 {
        Ok(requested)
    } else {
        Failure("Choose between 1 and 6 seats.")
    }
}

let result = ValidateSeats(3)
if let Ok(seats) = result {
    Console.WriteLine("Continue with ${seats} seats.")
} else {
    Console.WriteLine("Correct the seat count before continuing.")
}
// Continue with 3 seats.

if let binds seats only in the successful branch. The equivalent is pattern is if result is Ok(let seats) { ... } else { ... }.

Handle both cases with match

A form needs the actual error message, not just a success flag. Use match when each outcome contributes its own value. Both cases are handled explicitly.

let feedback = ValidateSeats(0) match {
    Ok(let seats) => "Ready to book ${seats} seats."
    Failure(let message) => message
}
Console.WriteLine(feedback)
// Choose between 1 and 6 seats.

Propagate Failure with ?

A price calculation cannot continue with an invalid seat count. ? extracts the value from Ok, or immediately returns the existing Failure from CalculateTotal. The error message is preserved. The final Ok wraps the calculated total; ? does not wrap successful return values automatically.

func CalculateTotal(requested: int) -> Result<int, string> {
    let seats = ValidateSeats(requested)?
    Ok(seats * 25)
}

let total = CalculateTotal(3) // Ok(75)
let invalid = CalculateTotal(0) // Failure("Choose between 1 and 6 seats.")
Console.WriteLine(total.UnwrapOr(0))
if invalid is Failure(let message) {
    Console.WriteLine(message)
} else {
    Console.WriteLine("Booking can continue.")
}
// 75
// Choose between 1 and 6 seats.

Use ? when this function cannot recover and its caller should receive the failure. Branch or match when you need to display, recover from, or translate it.

Propagate different error types with ?

Validation and payment can have different error contracts. An ad-hoc union in Result<int, SeatError | PaymentError> lets the booking operation preserve either error without converting it to a string or introducing another error wrapper. This example uses an explicit payment decision so both outcomes are easy to try.

record SeatError(val Message: string)
record PaymentError(val Message: string)

func ReserveSeats(requested: int) -> Result<int, SeatError> {
    return if requested >= 1 && requested <= 6 {
        Ok(requested)
    } else {
        Failure(SeatError("Choose between 1 and 6 seats."))
    }
}

func Charge(amount: int, approved: bool) -> Result<int, PaymentError> {
    return if approved {
        Ok(amount)
    } else {
        Failure(PaymentError("Payment declined."))
    }
}

func Book(requested: int, approved: bool) -> Result<int, SeatError | PaymentError> {
    let seats = ReserveSeats(requested)?
    let paid = Charge(seats * 25, approved)?
    Ok(paid)
}

let confirmation = Book(2, false) match {
    Ok(let paid) => "Booked for ${paid}."
    Failure(SeatError error) => "Update your request: ${error.Message}"
    Failure(PaymentError error) => "Try another payment method: ${error.Message}"
}
Console.WriteLine(confirmation)
// Try another payment method: Payment declined.

Each ? returns its original error through the enclosing result's error union. There is still one Failure case: its payload is either a SeatError or a PaymentError, and the typed patterns distinguish them. Book(0, true) returns a seat error before calling Charge; Book(2, true) returns Ok(50). Use a dedicated error union instead when you want a single named domain contract.

Type parameters

Name Description
T The successful value type.
E The error value type.

Cases

2 cases

Properties

6 items
HasError: bool Gets a value indicating whether this result is `Failure`.
HasOk: bool Gets a value indicating whether this result is `Ok`.
HasValue: bool Represents either a successful value (`Ok`) or an expected failure (`Failure`). Use `Result<T, E>` when the caller needs to act on a failure reason. For ordinary absence without an…
IsError: Optional<E> Gets the error value as an option.
IsOk: Optional<T> Gets the successful value as an option.
Value: object? Represents either a successful value (`Ok`) or an expected failure (`Failure`). Use `Result<T, E>` when the caller needs to act on a failure reason. For ordinary absence without an…

Methods

22 items
Equals(obj: object?) -> bool Indicates whether this instance and a specified object are equal. Inherited from ValueType.Equals
Static member: FromResidual(residual: E) -> Result<T, E> Constructs an error result from a propagated residual. Implements IPropagatable<Result<T, E>, T, E>.FromResidual
GetEnumerator() -> IEnumerator<T> Returns an enumerator over the zero-or-one successful values.
GetHashCode() -> int Returns the hash code for this instance. Inherited from ValueType.GetHashCode
GetType() -> Type Gets the System.Type of the current instance. Inherited from Object.GetType
Map<TResult>(mapper: T -> TResult) -> Result<TResult, E> Projects the value inside `Ok` using `mapper`. `Failure` stays `Failure`.
Match<TResult>(ok: T -> TResult, error: E -> TResult) -> TResult Matches the result and produces a value.
OrElse(recover: E -> Result<T, E>) -> Result<T, E> Recovers from an error by mapping it to a new result.
Tap(action: T -> ()) -> Result<T, E> Runs `action` if this is `Ok`, returning the original result.
TapError(action: E -> ()) -> Result<T, E> Runs `action` if this is `Failure`, returning the original result.
Then<TResult>(binder: T -> Result<TResult, E>) -> Result<TResult, E> Monadic bind / flatMap: chains a `Result`-producing function.
ToEnumerable() -> IEnumerable<T> Converts `Ok(x)` to a single-item sequence and `Failure` to an empty sequence.
ToString() -> string No summary available. Overrides ValueType.ToString
TryGetOutput(output: out T) -> bool Attempts to extract the successful output used by the propagation protocol. Implements IPropagatable<Result<T, E>, T, E>.TryGetOutput
TryGetResidual(residual: out E) -> bool Attempts to extract the residual used by the propagation protocol. Implements IPropagatable<Result<T, E>, T, E>.TryGetResidual
UnwrapError() -> E Returns the error value, or throws when this result is `Ok`.
UnwrapOr(defaultValue: T) -> T Returns the successful value, or `defaultValue` for an error.
UnwrapOrDefault() -> T Returns the successful value, or the default value for an error.
UnwrapOrElse(factory: () -> T) -> T Unwrap the value if Ok, otherwise return the value from `factory()`.
UnwrapOrThrow() -> T Returns the successful value, or throws for an error.
Extension member: WithContext(message: string) -> Result<T, ContextError<E>> Wraps only the error channel with operation context. The original typed error is preserved as the contextual error's cause. Extension from ResultErrorContextExtensions<T, E>.WithContextApplies to Result<T, E> where E: IError.